/**
 *  1. 作为对象的方法调用
 *  
 *  当函数作为对象的方法被调用时，this 指向该对象
 *  */

var obj = {
    a: 1,
    getA: function () {
        console.log(this === obj); // true
        console.log(this.a);       // 1
    }
};
obj.getA();

/**
 *  2. 作为普通函数调用
 *  
 *  当函数不作为对象的属性被调用时，也就是常说的普通函数方式，此时的 this 总是指向全局对象。
 *  在浏览器的 JavaScript 里，这个全局对象就是 window 对象。
 *  */
window.name = 'globalName';

var getName = function () {
    return this.name;
};

console.log(getName());  // globalName

// 或者：

window.name = 'globalName';

var myObject = {
    name: 'sven',
    getName: function () {
        return this.name;
    }
};

var getName = myObject.getName;

console.log(getName());  // globalName

// 在 ECMAScript5 的 strict 模式下，这种情况下的 this 已经被规定为不会指向全局对象，而是 undefined。

function func () {
    'use strict'
    console.log(this);
}
func();  // undefined


/**
 *  3. 构造器调用
 *  
 *  除了宿主提供的一些内置函数，大部分 JavaScript 函数都可以当作构造器使用。
 *  构造器的外表跟普通函数一模一样，它们的区别在于被调用的方式。
 *  当用 new 运算符调用函数时，该函数总会返回一个对象，通常情况下，构造器里的 this 就指向返回的这个对象。
 *  */

var MyClass = function () {
    this.name = 'sven';
};

var obj = new MyClass();

console.log(obj.name);   // sven

// 但用 new 调用构造器时，还要注意一个问题，如果构造器显式地返回了一个 object 类型地对象，那么此次运算结果最终会返回这个对象，而不是我们之前期待的 this

var MyClass = function () {
    this.name = 'sven';
    return { // 显式地返回一个对象
        name: 'anne'
    }
};

var obj = new MyClass();

console.log(obj.name); // anne

// 如果构造器不显式地返回任何数据，或者是返回一个非对象类型地数据，就不会造成上述问题

var MyClass = function () {
    this.name = 'sven';
    return 'anne'; // 返回 string 类型
};

var obj = new MyClass();

console.log(obj.name); // sven

/**
 *  4. Function.prototype.call 或 Function.prototype.apply 调用
 *  
 *  跟普通的函数调用相比，用 Function.prototype.call 或 Function.prototype.apply 可以动态地改变传入函数的 this
 *  */

var obj1 = {
    name: 'sven',
    getName: function () {
        return this.name;
    }
};

var obj2 = {
    name: 'anne'
};

console.log(obj1.getName()); // sven
console.log(obj1.getName.call(obj2));  // anne

// call 和 apply 方法能很好地体现 JavaScript 的函数式语言特性。
// 在 JavaScript 中，几乎每一次编写函数式语言风格的代码，都离不开 call 和 apply。